'''
Here is the okex api example.

'''
import logging
import os.path
import time
import warnings

import matplotlib

import okex.v5.account_api as account
import okex.v5.market_api as market
import okex.v5.trade_api as trade
from Indicators import ATRindicator
from okex.client_old import *
from okex.v5.trade_api import TdMode, OrderType
from strategies import macdStrategy,testStrategy
from tools.dingdingrobot import call_dingtalk
from tools.send_mail import send_email
from risk_control import chandelier_v1

matplotlib.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings("ignore")

# 全局变量
test = True


def get_timestamp():
    now = datetime.datetime.now()
    t = now.isoformat("T", "milliseconds")
    return t + "Z"


# 保证执行成功
def execute_decorator(func):
    def wrapper(*args, **kwargs):
        result = None
        fail_count = 0
        while True:
            try:
                result = func(*args, **kwargs)
                if func.__name__ == 'set_lever':
                    if int(result['code']) == 0:
                        call_dingtalk("杠杆设置成功", 'text', test=test)
                        return result
                elif func.__name__ == 'get_current_price':
                    if float(result) > 0:
                        call_dingtalk("当前价格获取结果为 {}".format(result), 'text', test=test)
                        return result
                else:
                    return result
            except:
                # logging.debug(str(result))
                time.sleep(5)
                fail_count += 1
                if fail_count == 5:
                    call_dingtalk("Execute failed, result is {}, func name is {}".format(result, func.__name__), 'text',
                                  test=test)

    return wrapper


@execute_decorator
def set_lever(account_api, instId, lever=3):
    call_dingtalk("设置杠杆倍数{}开始".format(lever), 'text', test=test)
    result_lever = account_api.set_leverage(instId=instId, lever=lever, mgnMode='cross')
    return result_lever


@execute_decorator
def get_current_price(marketAPI, instId):
    call_dingtalk("获取当前价格开始", 'text', test=test)
    result = marketAPI.ticker(instId).loc[0, 'last']
    return result


@execute_decorator
def get_history_candles(marketAPI, instId, days=1, bar='4H'):
    """

    :param marketAPI:
    :param instId:
    :param days:
    :param bar:
    :return:
    """
    call_dingtalk("获取历史数据开始", 'text', test=test)
    # 获取当前时间的毫秒级时间戳
    now = datetime.datetime.now()
    timestamp_now = int(now.timestamp() * 1000)
    # 获取days之前的时刻并将其转换为毫秒级时间戳
    two_months_ago = now - datetime.timedelta(days=days)
    timestamp_two_months_ago = int(two_months_ago.timestamp() * 1000)
    history_candles = marketAPI.history_candles(instId, before=timestamp_two_months_ago,
                                                after=timestamp_now, bar=bar)
    time_obj = history_candles.iloc[-1, 0]
    timestamp = int(datetime.datetime.strptime(str(time_obj), '%Y-%m-%d %H:%M:%S').timestamp()*1000)

    old_timestamp = None
    while timestamp - timestamp_two_months_ago:
        time.sleep(1)
        more_history_candles = marketAPI.history_candles(instId, before=timestamp_two_months_ago,
                                                            after=timestamp, bar=bar)
        history_candles = pd.concat([history_candles, more_history_candles])
        history_candles.reset_index(drop=True, inplace=True)
        time_obj = history_candles.iloc[-1, 0]
        timestamp = int(datetime.datetime.strptime(str(time_obj), '%Y-%m-%d %H:%M:%S').timestamp()*1000)
        if old_timestamp == None:
            old_timestamp = timestamp
        else:
            if old_timestamp == timestamp:
                break
            else:
                old_timestamp = timestamp
    return history_candles

@execute_decorator
def get_current_history_candles(marketAPI, instId, bar='4H'):
    # call_dingtalk("获取当前历史数据开始", 'text', test=test)
    history_candles = marketAPI.history_candles(instId, bar=bar)
    return history_candles


@execute_decorator
def get_position_info(accountAPI, instId):
    call_dingtalk("获取持仓信息开始", "text", test=test)
    position_info = accountAPI.positions(instId=instId)
    return position_info


@execute_decorator
def get_max_sell_or_sold(accountAPI, instId):
    call_dingtalk("获取最大可买/卖数量开始", "text", test=test)
    size_response = accountAPI.get_max_sell_or_sold(instId,
                                                    tdMode=TdMode.CROSS)
    return size_response


@execute_decorator
def trade_action(tradeAPI, instId, side, sz, px, tpTriggerPx=None, slTriggerPx=None, tpOrdPx=None, slOrdPx=None,
                 tdMode=TdMode.CROSS, ordType=OrderType.FOK):
    call_dingtalk("进行{}交易开始".format(side), "text", test=test)
    td_response = tradeAPI.order(instId=instId, tdMode=tdMode,
                                 ordType=ordType, side=side, sz=sz,
                                 px=px, tpTriggerPx=tpTriggerPx,
                                 tpOrdPx=tpOrdPx, slTriggerPx=slTriggerPx,
                                 slOrdPx=slOrdPx)
    return td_response


@execute_decorator
def trade_action_new(tradeAPI, instId, side, sz, px, tdMode=TdMode.CROSS, ordType=OrderType.FOK, attachAlgoOrds=None):
    call_dingtalk("进行{}交易开始".format(side), "text", test=test)
    td_response = tradeAPI.order_new(instId=instId, tdMode=tdMode,
                                     ordType=ordType, side=side, sz=sz,
                                     px=px, attachAlgoOrds=attachAlgoOrds)
    return td_response


@execute_decorator
def order_change(tradeAPI, instId, ordId, clOrdId=None, attachAlgoOrds=None):
    call_dingtalk("修改订单{}开始".format(ordId), "text", test=test)
    change_response = tradeAPI.change_order(instId=instId, ordId=ordId, clOrdId=clOrdId, attachAlgoOrds=attachAlgoOrds)
    return change_response


@execute_decorator
def order_get(tradeAPI, instId, ordId):
    call_dingtalk("获取订单{}结果开始".format(ordId), "text", test=test)
    change_response = tradeAPI.get_order(instId=instId, ordId=ordId)
    return change_response


@execute_decorator
def strategy_order(tradeAPI, instId, side, tpTriggerPx, slTriggerPx, sz, tpOrdPx='-1', slOrdPx='-1',
                   tdMode=TdMode.CROSS, ordType=OrderType.CONDITIONAL, attachAlgoOrds=None):
    call_dingtalk("设置止盈止损策略开始", "text", test=test)

    strategy_response = tradeAPI.strategy_order(instId=instId, side=side,
                                                tpTriggerPx=tpTriggerPx, slTriggerPx=slTriggerPx, sz=sz,
                                                tpOrdPx=tpOrdPx, slOrdPx=slOrdPx,
                                                tdMode=tdMode, ordType=ordType, attachAlgoOrds=attachAlgoOrds)
    return strategy_response


@execute_decorator
def strategy_order_change(tradeAPI, instId,
                          algoId: str,
                          newTpTriggerPx="0",
                          newSlTriggerPx="0"
                          ):
    call_dingtalk("修改止盈止损策略开始", "text", test=test)
    change_response = tradeAPI.strategy_order_change(instId=instId,
                                                     algoId=algoId,
                                                     newTpTriggerPx=newTpTriggerPx,
                                                     newSlTriggerPx=newSlTriggerPx)
    return change_response


if __name__ == '__main__':

    # param test's value is False if is True will use simulative trading

    base_dir = r"./"

    with open(os.path.join(base_dir, "passwords", "passwords.json"), 'r') as f:
        data = json.load(f)

    if test:
        # 模拟盘api
        api_key = data[0]['api_key']
        secret_key = data[0]['secret_key']
        passphrase = data[0]['passphrase']
    else:
        # 实盘API
        api_key = data[1]['api_key']
        secret_key = data[1]['secret_key']
        passphrase = data[1]['passphrase']

    obj = dict([('api_key', api_key), ('secret_key', secret_key), ('passphrase', passphrase)])

    api_key = obj['api_key']
    secret_key = obj['secret_key']
    passphrase = obj['passphrase']

    instId = 'BTC-USDT-SWAP'

    # 设置日志存储位置
    LOG_DIR = os.path.join(base_dir, "./log")
    if not os.path.exists(LOG_DIR):
        os.makedirs(LOG_DIR)
    LOG_ADDR = os.path.join(LOG_DIR, 'runningLog.log')
    logging.basicConfig(filename=LOG_ADDR,format='%(asctime)s - %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
    logger = logging.getLogger(__name__)
    logger.info("start")
    # handler = RotatingFileHandler(LOG_ADDR, maxBytes=10000, backupCount=2)
    # logger.addHandler(handler)

    # 设置杠杆倍数
    lever = 3
    # 设置ATR止损参数
    atrbase = 2  # atr止损基础
    win_loss_rate = 1.5  # 盈亏比
    win_point = None
    loss_point = None
    enter_price = None
    max_high = None
    min_low = None
    str_w_max = 1.0
    str_w_level1 = 0.05
    str_w_level2 = 0.15
    atr_w_limit = 0.2
    profit = None
    buy_price = None  # 多仓进场价格
    sell_price = None  # 空仓进场价格

    # 设置杠杆倍数
    accountAPI = account.AccountAPI(api_key, secret_key, passphrase, use_server_time=False, test=test)
    set_lever(accountAPI, instId, lever)

    # 持仓信息debug
    position_info = accountAPI.positions(instId=instId)

    direction = None
    latest_time = None
    highest_price = None
    lowest_price = None
    if test:
        bar = '1m'
    else:
        bar = '4H'
    # 上周期持仓
    last_pos_hold = None

    # 获取历史数据时间
    days = 60

    pic_dir = os.path.join(base_dir, "./pictures")
    if not os.path.exists(pic_dir):
        os.makedirs(pic_dir)
    count = 0
    side = None  # 初始没有买卖方向
    sz = 0  # 初始没有购买量
    buy_price = None
    sell_price = None
    algoId = None
    # 初始止损策略
    risk_strategy = chandelier_v1.Chandelier()
    while True:
        count += 1
        now = datetime.datetime.now()
        current_time = now.strftime("%Y-%m-%d %H:%M:%S")
        if count % 1000 == 0:
            send_email("周期{}的日志".format(count), LOG_ADDR)
        call_dingtalk("当前周期为{},时间为{}".format(count, current_time), 'text', test=test)
        logging.debug('loop_count: ' + str(count) + ' ' + "time: " + current_time)
        # 账户API
        accountAPI = account.AccountAPI(api_key, secret_key, passphrase, use_server_time=False, test=test)

        # 交易API
        tradeAPI = trade.TradeAPI(api_key, secret_key, passphrase, use_server_time=False, test=test)

        # 市场行情API
        marketAPI = market.MarketAPI(api_key, secret_key, passphrase, use_server_time=False, test=test)
        marketAPI.set_api_url("https://www.okx.com")
        current_price = get_current_price(marketAPI, instId)
        print("\r", current_price, end="")


        history_candles = get_current_history_candles(marketAPI, instId, bar=bar)
        # history_candles = get_history_candles(marketAPI, instId, bar=bar)
        # history_candles.to_csv("./data_info/bitcoin.csv")
        if test == True:
            history_candles = testStrategy.testStartegy(history_candles)
        else:
            history_candles = macdStrategy.macdstartegy(history_candles)

        atr_df = ATRindicator.ATR(history_candles)
        atr = atr_df.iloc[-1]['ATR']
        history_candles.sort_values(by="ts", ascending=True, inplace=True)

        history_candles.reset_index(drop=True, inplace=True)

        # 判断是否有持仓
        position_info = get_position_info(accountAPI, instId)
        if len(position_info) != 0:
            pos = int(position_info[0]['pos'])
            if pos == 0:
                side = None  # 初始没有买卖方向
                sz = 0  # 初始没有购买量
                buy_price = None
                sell_price = None
                algoId = None
        else:
            pos = 0
            side = None
            sz = 0
            buy_price = None
            sell_price = None
            algoId = None
        if pos == 0:
            risk_strategy.clear()
            # 判断信号是否出现
            # 买入信号出现
            if history_candles.iloc[-2]['signal'] == 1:
                size_response = get_max_sell_or_sold(accountAPI, instId)
                maxBuy = int(float(size_response[0]['maxBuy']))
                call_dingtalk("最大可买数量{}".format(maxBuy), "text", test=test)
                # 执行买入操作,设置初始止损
                if maxBuy > 0:
                    buy_price = min(current_price, history_candles.iloc[-2]['c'])  # 买入价格选择当前价格和开盘价格的最低点
                    side = 'buy'
                    sz = maxBuy
                    enter_price = buy_price
                    max_high = history_candles.iloc[-1]['h']
                    td_response = trade_action(tradeAPI,
                                               instId,
                                               side=side,
                                               sz=sz,
                                               px=str(buy_price)
                                               )
                    order_id = td_response['data'][0]['ordId']
                    # 获取订单是否成交状态
                    get_order_result = order_get(tradeAPI, instId, order_id)
                    logger.info("buy order result is:{}".format(get_order_result))
                    if len(get_order_result) == 0:
                        continue
                    # 多单动态ATR吊灯止损
                    time.sleep(5)
                    # 订单成交
                    if get_order_result[0]['state'] == "filled":
                        risk_strategy.set_enter_point(enter_price)
                        win_point = risk_strategy.get_profit_point(history_candles, 1)
                        loss_point = risk_strategy.get_loss_point(history_candles, 1)
                        change_response = strategy_order(tradeAPI,
                                                         instId=instId,
                                                         side='sell',
                                                         sz=sz,
                                                         tpTriggerPx=None,  # 止盈触发价格,关闭则不设置止盈
                                                         tpOrdPx=None,
                                                         slOrdPx=str(loss_point),
                                                         slTriggerPx=str(loss_point)
                                                         )
                        logger.info("Buy order stop profit result:{}".format(change_response))
                        algoId = change_response['data'][0]['algoId']


            elif history_candles.iloc[-2]['signal'] == 2:
                size_response = get_max_sell_or_sold(accountAPI, instId)
                maxSell = int(float(size_response[0]['maxSell']))
                call_dingtalk("最大可卖数量{}".format(maxSell), "text", test=test)
                if maxSell > 0:
                    side = 'sell'
                    sell_price = max(current_price, history_candles.iloc[-1]['o'])
                    enter_price = sell_price
                    min_low = history_candles.iloc[-1]['l']
                    td_response = trade_action(tradeAPI,
                                               instId,
                                               side=side,
                                               sz=maxSell,
                                               px=str(sell_price))
                    order_id = td_response['data'][0]['ordId']
                    # 获取订单是否成交状态
                    get_order_result = order_get(tradeAPI, instId, order_id)
                    logger.info("Sell order result is:{}".format(get_order_result))
                    if len(get_order_result) == 0:
                        continue
                    # 空单动态ATR吊灯止损
                    time.sleep(5)
                    # 订单成交
                    if get_order_result[0]['state'] == "filled":
                        risk_strategy.set_enter_point(enter_price)
                        win_point = risk_strategy.get_profit_point(history_candles, 0)
                        loss_point = risk_strategy.get_loss_point(history_candles, 0)
                        change_response = strategy_order(tradeAPI,
                                                         instId=instId,
                                                         side='buy',
                                                         sz=sz,
                                                         tpTriggerPx=None,  # 止盈触发价格,关闭则不设置止盈
                                                         tpOrdPx=None,
                                                         slOrdPx=str(loss_point),
                                                         slTriggerPx=str(loss_point)
                                                         )
                        logger.info("Sell order stop profit result is:{}".format(change_response))
                        algoId = change_response['data'][0]['algoId']

        else:
            # 拥有持仓,设置动态止盈止损
            if side == 'buy':
                # 动态止损
                if algoId is not None:
                    win_point = risk_strategy.get_profit_point(history_candles, 1)
                    loss_point = risk_strategy.get_loss_point(history_candles, 1)
                    current_price = get_current_price(marketAPI, instId)
                    logger.info("new loss point is {}, current price is {}, algoId is {}".format(loss_point,current_price,algoId))
                    if loss_point < current_price:
                        change_order_response = strategy_order_change(tradeAPI,
                                                                      instId=instId,
                                                                      algoId=algoId,
                                                                      newSlTriggerPx=str(loss_point))
                        logger.info("buy change stop order result is:{}".format(change_order_response))
                else:
                    win_point = risk_strategy.get_profit_point(history_candles, 1)
                    loss_point = risk_strategy.get_loss_point(history_candles, 1)
                    logger.info("loss point is {}".format(loss_point))
                    change_response = strategy_order(tradeAPI,
                                                     instId=instId,
                                                     side=side,
                                                     sz=sz,
                                                     tpTriggerPx=None,  # 止盈触发价格,关闭则不设置止盈
                                                     tpOrdPx=None,
                                                     slOrdPx=str(loss_point),
                                                     slTriggerPx=str(loss_point)
                                                     )
                    algoId = change_response['data'][0]['algoId']
                    logger.info("stop profit order is {}".format(change_response))
            elif side == 'sell':
                # 空单止损
                if algoId is not None:
                    win_point = risk_strategy.get_profit_point(history_candles, 0)
                    loss_point = risk_strategy.get_loss_point(history_candles, 0)
                    current_price = get_current_price(marketAPI, instId)
                    logger.info("new loss point is {}, current price is {}".format(loss_point,current_price))
                    if loss_point > current_price:
                        change_order_response = strategy_order_change(tradeAPI,
                                                                      instId=instId,
                                                                      algoId=algoId,
                                                                      newSlTriggerPx=str(loss_point))
                        logger.info("sell change stop order result is:{}".format(change_order_response))
                else:
                    win_point = risk_strategy.get_profit_point(history_candles, 0)
                    loss_point = risk_strategy.get_loss_point(history_candles, 0)
                    logger.info("new loss point is {}".format(loss_point))
                    change_response = strategy_order(tradeAPI,
                                                     instId=instId,
                                                     side=side,
                                                     sz=sz,
                                                     tpTriggerPx=None,  # 止盈触发价格,关闭则不设置止盈
                                                     tpOrdPx=None,
                                                     slOrdPx=str(loss_point),
                                                     slTriggerPx=str(loss_point)
                                                     )
                    algoId = change_response['data'][0]['algoId']
                    logger.info("stop profit order is {}".format(change_response))

